<!DOCTYPE html>
<html>
<head>
<title>RTCPeerConnection.getSenders</title>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<script>
promise_test(async t => {
  const pc = new RTCPeerConnection();
  t.add_cleanup(() => pc.close());
  return createStreams({audio:true}, 1)
    .then(function(streams) {
      for (let i = 0; i < streams.length; ++i) {
        pc.addStream(streams[i]);
      }
      verifyStreamAndTrackCounts(pc.getLocalStreams(), 1, true, false);
      verifyLocalStreamTracksHaveSenders(pc);
      // Make sure object identities are preserved between calls.
      assert_array_equals(pc.getSenders(), pc.getSenders());
    });
}, 'getSenders() for a stream with a single audio track.');

promise_test(async t => {
  const pc = new RTCPeerConnection();
  t.add_cleanup(() => pc.close());
  return createStreams({video:true}, 1)
    .then(function(streams) {
      for (let i = 0; i < streams.length; ++i) {
        pc.addStream(streams[i]);
      }
      verifyStreamAndTrackCounts(pc.getLocalStreams(), 1, false, true);
      verifyLocalStreamTracksHaveSenders(pc);
      // Make sure object identities are preserved between calls.
      assert_array_equals(pc.getSenders(), pc.getSenders());
    });
}, 'getSenders() for a stream with a single video track.');

promise_test(async t => {
  const pc = new RTCPeerConnection();
  t.add_cleanup(() => pc.close());
  return createStreams({audio:true, video:true}, 1)
    .then(function(streams) {
      for (let i = 0; i < streams.length; ++i) {
        pc.addStream(streams[i]);
      }
      verifyStreamAndTrackCounts(pc.getLocalStreams(), 1, true, true);
      verifyLocalStreamTracksHaveSenders(pc);
      // Make sure object identities are preserved between calls.
      assert_array_equals(pc.getSenders(), pc.getSenders());
    });
}, 'getSenders() for a stream with an audio and video track.');

promise_test(async t => {
  const pc = new RTCPeerConnection();
  t.add_cleanup(() => pc.close());
  return createStreams({audio:true, video:true}, 2)
    .then(function(streams) {
      for (let i = 0; i < streams.length; ++i) {
        pc.addStream(streams[i]);
      }
      verifyStreamAndTrackCounts(pc.getLocalStreams(), 2, true, true);
      verifyLocalStreamTracksHaveSenders(pc);
      // Make sure object identities are preserved between calls.
      assert_array_equals(pc.getSenders(), pc.getSenders());
    });
}, 'getSenders() for a multiple audio-video streams.');

promise_test(async t => {
  const pc = new RTCPeerConnection({sdpSemantics:'plan-b'});
  t.add_cleanup(() => pc.close());
  let senders = new Set();
  return createStreams({audio:true, video:true}, 2)
    .then(function(streams) {
      for (let i = 0; i < streams.length; ++i) {
        pc.addStream(streams[i]);
      }
      verifyStreamAndTrackCounts(pc.getLocalStreams(), 2, true, true);
      verifyLocalStreamTracksHaveSenders(pc);
      // Make sure object identities are preserved between calls.
      assert_array_equals(pc.getSenders(), pc.getSenders());

      for (let i = 0; i < pc.getSenders().length; ++i)
        senders.add(pc.getSenders()[i]);
      // 2 senders per stream, one for audio and one for video
      assert_equals(senders.size, 4);

      pc.removeStream(pc.getLocalStreams()[0]);
      verifyStreamAndTrackCounts(pc.getLocalStreams(), 1, true, true);
      verifyLocalStreamTracksHaveSenders(pc);
      // Make sure object identities are preserved between calls.
      assert_array_equals(pc.getSenders(), pc.getSenders());

      for (let i = 0; i < pc.getSenders().length; ++i)
        assert_true(senders.has(pc.getSenders()[i]));

      return createStreams({audio:true, video:true}, 1);
    })
    .then(function(streams) {
      for (let i = 0; i < streams.length; ++i) {
        pc.addStream(streams[i]);
      }
      verifyStreamAndTrackCounts(pc.getLocalStreams(), 2, true, true);
      verifyLocalStreamTracksHaveSenders(pc);
      // Make sure object identities are preserved between calls.
      assert_array_equals(pc.getSenders(), pc.getSenders());

      // |senders| contains all senders so far (4), including the ones for
      // the removed streams. The set does not contain duplicates, so adding all
      // senders here should only increase the size for the new stream (2 new
      // senders).
      for (let i = 0; i < pc.getSenders().length; ++i)
        senders.add(pc.getSenders()[i]);
      assert_equals(senders.size, 6);

      while (pc.getLocalStreams().length > 0) {
        pc.removeStream(pc.getLocalStreams()[0]);
      }
      assert_equals(pc.getLocalStreams().length, 0);
      assert_equals(pc.getSenders().length, 0);
    });
}, 'getSenders() for streams being added and removed.');

promise_test(async t => {
  const pc = new RTCPeerConnection({sdpSemantics:'plan-b'});
  t.add_cleanup(() => pc.close());
  return createStreams({audio:true, video:false}, 1)
    .then(function(streams) {
      let track = streams[0].getAudioTracks()[0];
      let sender = pc.addTrack(track);
      verifyTracksHaveSenders(pc, new Set([ track ]));
      assert_equals(pc.getSenders()[0], sender);
      pc.removeTrack(sender);
      assert_equals(pc.getSenders().length, 0);
    });
}, 'getSenders() for addTrack() and removeTrack().');

/**
 * Helper functions to tests.
 */

function createStreams(constraints, numStreams, streamsSoFar = []) {
  if (numStreams == 0) {
    return Promise.resolve(streamsSoFar);;
  }
  return navigator.mediaDevices.getUserMedia(constraints)
    .then(function(stream) {
      return createStreams(constraints,
                           numStreams - 1,
                           streamsSoFar.concat([stream]));
    });
}

function verifyStreamAndTrackCounts(streams, streamCount, audio, video) {
  assert_equals(streams.length, streamCount);
  for (let i = 0; i < streams.length; ++i) {
    assert_equals(streams[i].getAudioTracks().length, audio ? 1 : 0);
    assert_equals(streams[i].getVideoTracks().length, video ? 1 : 0);
  }
}

function verifyLocalStreamTracksHaveSenders(pc) {
  let localTracks = new Set();
  let localStreams = pc.getLocalStreams();
  for (let i = 0; i < localStreams.length; ++i) {
    let localStreamTracks = localStreams[i].getTracks();
    for (let j = 0; j < localStreamTracks.length; ++j) {
      localTracks.add(localStreamTracks[j]);
    }
  }
  return verifyTracksHaveSenders(pc, localTracks);
}

function verifyTracksHaveSenders(pc, tracks) {
  let senderTracks = new Set();
  let senders = pc.getSenders();
  for (let i = 0; i < senders.length; ++i) {
    assert_true(senders[i] != null);
    assert_true(senders[i].track != null);
    assert_true(tracks.has(senders[i].track));
    assert_false(senderTracks.has(senders[i].track));
    senderTracks.add(senders[i].track);
  }
  assert_equals(senderTracks.size, tracks.size);
}
</script>
</body>
</html>
